#include "StdAfx.h"
#include <JeayeSDK/JStringList.h>
#include <typeinfo>

JStringList::JStringList(INT_PTR nBlockSize /*= 10*/)
{
	_ASSERT(nBlockSize > 0);

	m_nCount = 0;
	m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL;
	m_pBlocks = NULL;
	m_nBlockSize = nBlockSize;
}

JStringList::~JStringList(void)
{
	RemoveAll();
	_ASSERT(m_nCount == 0);
}

JString JStringList::RemoveHead()
{
	_ASSERT(m_pNodeHead!=NULL);
	if(m_pNodeHead == NULL) throw 0;

	CNode* pOldNode = m_pNodeHead;
	JString returnValue = pOldNode->data;

	m_pNodeHead = pOldNode->pNext;
	if (m_pNodeHead != NULL)
		m_pNodeHead->pPrev = NULL;
	else
		m_pNodeTail = NULL;
	FreeNode(pOldNode);
	return returnValue;
}

JString JStringList::RemoveTail()
{
	_ASSERT(m_pNodeTail != NULL);  // don't call on empty list !!!
	if(m_pNodeTail==NULL) throw 0;

	CNode* pOldNode = m_pNodeTail;
	JString returnValue = pOldNode->data;

	m_pNodeTail = pOldNode->pPrev;
	if (m_pNodeTail != NULL)
		m_pNodeTail->pNext = NULL;
	else
		m_pNodeHead = NULL;
	FreeNode(pOldNode);
	return returnValue;
}

POSITION JStringList::AddHead(LPCTSTR newElement)
{
	return AddHead(JString(newElement));
}

POSITION JStringList::AddHead(const JString& newElement)
{
	CNode* pNewNode = NewNode(NULL, m_pNodeHead);
	pNewNode->data = newElement;
	if (m_pNodeHead != NULL)
		m_pNodeHead->pPrev = pNewNode;
	else
		m_pNodeTail = pNewNode;
	m_pNodeHead = pNewNode;
	return (POSITION) pNewNode;
}

void JStringList::AddHead(JStringList* pNewList)
{
	_ASSERT(pNewList!=NULL);
	if (pNewList == NULL)
	{
		throw 0;
	}
	
	// add a list of same elements to head (maintain order)
	POSITION pos = pNewList->GetTailPosition();
	while (pos != NULL)
		AddHead(pNewList->GetPrev(pos));
}

POSITION JStringList::AddTail(LPCTSTR newElement)
{
	return AddTail(JString(newElement));
}

POSITION JStringList::AddTail(const JString& newElement)
{
	CNode* pNewNode = NewNode(m_pNodeTail, NULL);
	pNewNode->data = newElement;
	if (m_pNodeTail != NULL)
		m_pNodeTail->pNext = pNewNode;
	else
		m_pNodeHead = pNewNode;
	m_pNodeTail = pNewNode;
	return (POSITION) pNewNode;
}

void JStringList::AddTail(JStringList* pNewList)
{
	_ASSERT(pNewList!=NULL);
	if (pNewList == NULL)
	{
		throw 0;
	}
	_ASSERT(strcmp(typeid(*pNewList).name(),typeid(JStringList).name())==0);
	// add a list of same elements
	POSITION pos = pNewList->GetHeadPosition();
	while (pos != NULL)
		AddTail(pNewList->GetNext(pos));
}

void JStringList::RemoveAll()
{
	_ASSERT(this);
	// destroy elements

	CNode* pNode;
	for (pNode = m_pNodeHead; pNode != NULL; pNode = pNode->pNext)
		DestructElement(&pNode->data);


	m_nCount = 0;
	m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL;
	m_pBlocks->FreeDataChain();
	m_pBlocks = NULL;
}

void JStringList::RemoveAt(POSITION position)
{
	CNode* pOldNode = (CNode*) position;
	_ASSERT(pOldNode!=NULL);
	if (pOldNode == NULL)
	{
		throw 0;
	}
	// remove pOldNode from list
	if (pOldNode == m_pNodeHead)
	{
		m_pNodeHead = pOldNode->pNext;
	}
	else
	{
		_ASSERT(strcmp(typeid(*pOldNode->pPrev).name(),typeid(CNode).name())==0);
		pOldNode->pPrev->pNext = pOldNode->pNext;
	}
	if (pOldNode == m_pNodeTail)
	{
		m_pNodeTail = pOldNode->pPrev;
	}
	else
	{
		_ASSERT(strcmp(typeid(*pOldNode->pNext).name(),typeid(CNode).name())==0);
		pOldNode->pNext->pPrev = pOldNode->pPrev;
	}
	FreeNode(pOldNode);
}

POSITION JStringList::InsertBefore(POSITION position, LPCTSTR newElement)
{
	return InsertBefore(position, JString(newElement));
}

POSITION JStringList::InsertBefore(POSITION position, const JString& newElement)
{
	if (position == NULL)
		return AddHead(newElement); // insert before nothing -> head of the list

	// Insert it before position
	CNode* pOldNode = (CNode*) position;
	CNode* pNewNode = NewNode(pOldNode->pPrev, pOldNode);
	pNewNode->data = newElement;

	if (pOldNode->pPrev != NULL)
	{
		_ASSERT(strcmp(typeid(*pOldNode->pPrev).name(),typeid(CNode).name())==0);
		pOldNode->pPrev->pNext = pNewNode;
	}
	else
	{
		_ASSERT(pOldNode == m_pNodeHead);
		m_pNodeHead = pNewNode;
	}
	pOldNode->pPrev = pNewNode;
	return (POSITION) pNewNode;
}

POSITION JStringList::InsertAfter(POSITION position, LPCTSTR newElement)
{
	return InsertAfter(position, JString(newElement));
}

POSITION JStringList::InsertAfter(POSITION position, const JString& newElement)
{
	if (position == NULL)
		return AddTail(newElement); // insert after nothing -> tail of the list

	// Insert it before position
	CNode* pOldNode = (CNode*) position;
	_ASSERT(strcmp(typeid(*pOldNode).name(),typeid(CNode).name())==0);
	CNode* pNewNode = NewNode(pOldNode, pOldNode->pNext);
	pNewNode->data = newElement;

	if (pOldNode->pNext != NULL)
	{
		_ASSERT(strcmp(typeid(*pOldNode->pNext).name(),typeid(CNode).name())==0);
		pOldNode->pNext->pPrev = pNewNode;
	}
	else
	{
		_ASSERT(pOldNode == m_pNodeTail);
		m_pNodeTail = pNewNode;
	}
	pOldNode->pNext = pNewNode;
	return (POSITION) pNewNode;
}

POSITION JStringList::Find(LPCTSTR searchValue, POSITION startAfter /*= NULL*/) const
{
	CNode* pNode = (CNode*) startAfter;
	if (pNode == NULL)
	{
		pNode = m_pNodeHead;  // start at head
	}
	else
	{
		pNode = pNode->pNext;  // start after the one specified
	}

	for (; pNode != NULL; pNode = pNode->pNext)
		if (pNode->data == searchValue)
			return (POSITION) pNode;
	return NULL;
}

POSITION JStringList::FindIndex(INT_PTR nIndex) const
{
	if (nIndex >= m_nCount || nIndex < 0)
		return NULL;  // went too far

	CNode* pNode = m_pNodeHead;
	while (nIndex--)
	{
		pNode = pNode->pNext;
	}
	return (POSITION) pNode;
}

JStringList::CNode* JStringList::NewNode(CNode* pPrev, CNode* pNext)
{
	if (m_pNodeFree == NULL)
	{
		// add another block
		JPlex* pNewBlock = JPlex::Create(m_pBlocks, m_nBlockSize,
			sizeof(CNode));

		// chain them into free list
		CNode* pNode = (CNode*) pNewBlock->data();
		// free in reverse order to make it easier to debug
		pNode += m_nBlockSize - 1;
		for (INT_PTR i = m_nBlockSize-1; i >= 0; i--, pNode--)
		{
			pNode->pNext = m_pNodeFree;
			m_pNodeFree = pNode;
		}
	}
	_ASSERT(m_pNodeFree != NULL);  // we must have something

	JStringList::CNode* pNode = m_pNodeFree;
	m_pNodeFree = m_pNodeFree->pNext;
	pNode->pPrev = pPrev;
	pNode->pNext = pNext;
	m_nCount++;
	_ASSERT(m_nCount > 0);  // make sure we don't overflow


	ConstructElement(&pNode->data);



	return pNode;
}

void JStringList::FreeNode(CNode* pNode)
{
	if (pNode == NULL)
	{
		throw 0;
	}

	DestructElement(&pNode->data);

	pNode->pNext = m_pNodeFree;
	m_pNodeFree = pNode;
	m_nCount--;
	_ASSERT(m_nCount >= 0);  // make sure we don't underflow

	// if no more elements, cleanup completely
	if (m_nCount == 0)
		RemoveAll();
}
